/*
 * Copyright (C) 2016 mzlion(and.mz.yq@gmail.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mzlion.easyokhttp;

import com.mzlion.core.lang.ArrayUtils;
import com.mzlion.core.lang.Assert;
import com.mzlion.core.lang.StringUtils;
import com.mzlion.core.utils.PlaceholderPropertyResolver;
import com.mzlion.core.utils.PropertyResolver;
import com.mzlion.easyokhttp.cookie.DefaultCookieJar;
import com.mzlion.easyokhttp.cookie.MemoryCookieStore;
import com.mzlion.easyokhttp.request.*;
import com.mzlion.easyokhttp.utils.CustomTrust;
import okhttp3.OkHttpClient;
import okio.Buffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 2016-05-12 Create class.
 * </p>
 *
 * @author mzlion
 */
public enum HttpClient {
    INSTANCE {};

    //logger
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class);

    /* Okhttp构造器 */
    private OkHttpClient.Builder builder;

    /* 默认的Header存储 */
    private Map<String, String> defaultHeaders;

    /* default constructor */
    HttpClient() {
        //default init
        builder = new OkHttpClient.Builder()
                //设置cookie自动管理
                .cookieJar(new DefaultCookieJar(new MemoryCookieStore()))
                //设置默认https验证
                .hostnameVerifier(new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                });

        //读取默认配置文件
        PropertyResolver propertyResolver = new PlaceholderPropertyResolver.Builder()
                .path("classpath:easy-okhttp.properties").build();
        builder
                //连接超时时间
                .connectTimeout(propertyResolver.getProperty("okhttp.connectionTimeout", Long.class), TimeUnit.MILLISECONDS)
                //读取超时时间
                .readTimeout(propertyResolver.getProperty("okhttp.readTimeout", Long.class), TimeUnit.MILLISECONDS);

        defaultHeaders = new ConcurrentHashMap<>(10);
    }

    /**
     * 设置连接超时时间
     *
     * @param connectionTimeout 超时时间，单位毫秒
     * @return {@linkplain HttpClient}
     */
    public HttpClient setConnectionTimeout(int connectionTimeout) {
        if (connectionTimeout <= 0) {
            LOGGER.error(" ===> Connect timeout must not be less than 0.");
            return this;
        }
        this.builder.connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS);
        return this;
    }

    /**
     * 设置流读取时间
     *
     * @param readTimeout 读取超时时间，单位毫秒
     * @return {@linkplain HttpClient}
     */
    public HttpClient setReadTimeout(int readTimeout) {
        if (readTimeout <= 0) {
            LOGGER.error(" ===> Read timeout must not be less than 0.");
            return this;
        }
        this.builder.readTimeout(readTimeout, TimeUnit.MILLISECONDS);
        return this;
    }

    public HttpClient setCertificates(String... certificateContents) {
        Assert.notEmpty(certificateContents, "The certificate content must not be null.");
        int length = certificateContents.length;
        InputStream[] certificateIns = new InputStream[length];
        for (int i = 0; i < length; i++) {
            certificateIns[i] = new Buffer().writeUtf8(certificateContents[i]).inputStream();
        }
        return this.setCertificates(certificateIns);
    }

    /**
     * 加载自定义SSL证书
     *
     * @param certificates 证书输入流
     * @return {@link HttpClient}
     */
    public HttpClient setCertificates(InputStream... certificates) {
        this.builder.sslSocketFactory(CustomTrust.sslSocketFactory(certificates));
        return this;
    }

    /**
     * 信任所有Https请求
     *
     * @return {@link HttpClient}
     */
    public HttpClient setCertificates() {
        return this.setCertificates((InputStream[]) null);
    }

    /**
     * 载入自定义SSL文件
     *
     * @param certificateFiles SSL文件
     * @return {@linkplain HttpClient}
     */
    public HttpClient setCertificates(File... certificateFiles) {
        Assert.notEmpty(certificateFiles, "Certificate files must not be null.");
        InputStream[] certificateIns = null;
        if (ArrayUtils.isNotEmpty(certificateFiles)) {
            int length = certificateFiles.length;
            certificateIns = new InputStream[length];
            for (int i = 0; i < length; i++) {
                try {
                    certificateIns[i] = new FileInputStream(certificateFiles[i]);
                } catch (FileNotFoundException e) {
                    throw new HttpClientConfigException(e);
                }
            }
        }
        return this.setCertificates(certificateIns);
    }

    /**
     * 设置默认的Http Header
     *
     * @param name  Header名称
     * @param value Header值
     * @return {@linkplain HttpClient}
     */
    public HttpClient setDefaultHeader(String name, String value) {
        if (StringUtils.hasText(name) && StringUtils.hasLength(value)) {
            this.defaultHeaders.put(name, value);
        }
        return this;
    }

    /**
     * 获取默认的Http Header列表
     *
     * @return Header键值对列表
     */
    public Map<String, String> getDefaultHeaders() {
        return defaultHeaders;
    }


    //========================构建OkHttpClient========================

    /**
     * 返回{@linkplain OkHttpClient}对象
     *
     * @return {@link OkHttpClient}
     */
    public OkHttpClient getOkHttpClient() {
        return builder.build();
    }


    //=====================request======================

    /**
     * Get请求
     *
     * @param url 请求地址
     * @return {@link GetRequest}
     */
    public static GetRequest get(String url) {
        return new GetRequest(url);
    }

    /**
     * X-WWW-FORM-URLENCODED表单提交
     *
     * @param url 提交地址
     * @return {@link CommonPostRequest}
     */
    public static CommonPostRequest post(String url) {
        return new CommonPostRequest(url);
    }

    /**
     * FORM-DATA表单提交
     *
     * @param url 提交地址
     * @return {@link FormDataPostRequest}
     */
    public static FormDataPostRequest formDataPost(String url) {
        return new FormDataPostRequest(url);
    }

    /**
     * 向请求体中传入二进制流
     *
     * @param url 请求地址
     * @return {@linkplain BinaryBodyPostRequest}
     */
    public static BinaryBodyPostRequest binaryBody(String url) {
        return new BinaryBodyPostRequest(url);
    }

    /**
     * 文本POST请求体
     *
     * @param url 请求地址
     * @return {@link TextBodyRequest}
     */
    public static TextBodyRequest textBody(String url) {
        return new TextBodyRequest(url);
    }
}
